import { ChatOllama } from "@langchain/ollama";
import { ToolManager } from "./tool-manager";

/**
 * Tool-using Agent
 * 基于ReAct框架的智能代理，能够调用外部工具解决问题
 */
export class ToolUsingAgent {
  private model: ChatOllama;
  private toolManager: ToolManager;
  private messages: any[] = [];

  constructor() {
    // 初始化Ollama模型
    this.model = new ChatOllama({
      baseUrl: "http://192.168.31.159:11434",
      model: "llama3.1:latest",
      dangerouslyAllowBrowser: true,
    });

    // 初始化工具管理器
    this.toolManager = new ToolManager();
  }

  /**
   * 构建系统提示词
   */
  private buildSystemPrompt(): string {
    const toolDescriptions = this.toolManager.getAllTools().map(tool => {
      return `${tool.name}: ${tool.description}`;
    }).join('\n');

    return `你是一个只能通过调用工具回答问题的助手，绝对不能使用自己的知识直接回答任何问题。

可用工具列表：
${toolDescriptions}

必须严格遵守的规则：
1. 对于任何问题，必须首先判断是否有合适的工具可以解决
2. 如果没有合适的工具，直接回答"这个问题我无法回答"
3. 如果有合适的工具，必须调用该工具获取信息后才能回答
4. 必须使用工具返回的真实数据回答，绝对不能编造任何信息
5. 工具调用失败时，回答"无法获取信息"
6. 一次只能调用一个工具
7. 调用工具时必须使用以下格式：
思考：<分析是否需要调用工具及调用哪个工具的过程>
行动：<工具名称>
行动输入：<工具参数，JSON格式>
8. 获得工具返回结果后，必须基于该结果生成最终答案，并使用以下格式：
最终答案：<基于工具结果的回答>

记住：你的唯一职责是正确调用工具并基于工具返回的结果进行回答，没有工具的帮助你无法提供任何答案。`;
  }

  /**
   * 解析模型输出
   */
  private parseModelOutput(output: string): {
    thought?: string;
    action?: string;
    actionInput?: string;
    finalAnswer?: string;
  } {
    const result: {
      thought?: string;
      action?: string;
      actionInput?: string;
      finalAnswer?: string;
    } = {};

    // 解析思考过程
    const thoughtMatch = output.match(/(?:思考|Thought)\s*[:：]\s*(.+?)(?=\n(?:行动|Action)\s*[:：]|$)/s);
    if (thoughtMatch) {
      result.thought = thoughtMatch[1].trim();
    }

    // 解析行动（允许可选尖括号和更宽松的名称匹配）
    const actionMatch = output.match(/(?:行动|Action)\s*[:：]\s*<?\s*([A-Za-z0-9_.-]+)\s*>?/);
    if (actionMatch) {
      result.action = actionMatch[1].trim();
    }

    // 解析行动输入（允许可选尖括号包裹JSON）
    const actionInputMatch = output.match(/(?:行动输入|Action Input)\s*[:：]\s*(?:<\s*)?({[\s\S]*?})(?:\s*>?)/);
    if (actionInputMatch) {
      result.actionInput = actionInputMatch[1].trim();
    }

    // 解析最终答案
    const finalAnswerMatch = output.match(/(?:最终答案|Final Answer)\s*[:：]\s*([\s\S]+)$/);
    if (finalAnswerMatch) {
      result.finalAnswer = finalAnswerMatch[1].trim();
    }

    return result;
  }

  /**
   * 规范化并修正工具参数
   */
  private normalizeArgs(toolName: string, rawArgs: any): any {
    // 仅处理对象类型
    const args = typeof rawArgs === 'object' && rawArgs !== null ? { ...rawArgs } : rawArgs;

    // 针对天气工具的参数名称修正：city -> location
    if (toolName === 'getCurrentWeather' && args && typeof args === 'object') {
      if (args.city && !args.location) {
        args.location = args.city;
        delete args.city;
      }
    }

    return args;
  }

  /**
   * 运行Agent
   */
  async run(userInput: string): Promise<string> {
    console.log(`🤔 问题：${userInput}`);

    this.messages = [
      { role: "system", content: this.buildSystemPrompt() },
      { role: "user", content: userInput }
    ];

    let iterations = 0;
    const maxIterations = 3; // 减少迭代次数，避免不必要的循环
    let toolResult: string | null = null;

    while (iterations < maxIterations) {
      console.log(`\n🔄 第 ${iterations + 1} 轮思考...`);

      // 调用模型
      const response = await this.model.invoke(this.messages);
      const content = response.content as string;
      console.log(`🤖 模型输出：\n${content}`);

      // 解析输出
      const parsed = this.parseModelOutput(content);

      // 优先执行工具调用（即使同时出现最终答案）
      if (parsed.action && parsed.action !== "无" && parsed.actionInput) {
        // 检查工具是否存在
        const tool = this.toolManager.getTool(parsed.action);
        if (!tool) {
          console.log(`⚠️ 工具不存在：${parsed.action}`);
          return "无法回答";
        }

        console.log(`🔧 调用工具：${parsed.action}`);

        try {
          // 解析工具参数
          let actionInput;
          try {
            actionInput = JSON.parse(parsed.actionInput);
          } catch (e) {
            throw new Error(`无效的参数格式，需要JSON格式: ${parsed.actionInput}`);
          }

          // 规范化参数（例如将 city 替换为 location）
          const normalizedArgs = this.normalizeArgs(parsed.action, actionInput);

          // 使用工具schema做一次轻量校验并提供清晰错误
          const schema: any = (tool as any).schema;
          if (schema && typeof schema.safeParse === 'function') {
            const validation = schema.safeParse(normalizedArgs);
            if (!validation.success) {
              console.error('❌ 参数不匹配：', validation.error?.message);
              return "无法获取信息：参数无效，请使用 { \"location\": \"北京\", \"unit\"?: \"celsius\"|\"fahrenheit\" }";
            }
          }

          toolResult = await this.toolManager.callTool(parsed.action, normalizedArgs);
          console.log(`📊 工具返回：${toolResult}`);

          // 将工具结果添加到消息中
          this.messages.push({ role: "assistant", content });
          this.messages.push({ role: "user", content: `工具返回结果：${toolResult}\n请基于该结果生成最终答案。\n格式：\n最终答案：<你的回答>` });
          iterations++;
          continue; // 继续下一轮以获取最终答案
        } catch (error) {
          console.error(`❌ 工具调用失败：${error}`);
          return "无法获取信息";
        }
      }

      // 只有在已有工具结果的情况下才接受最终答案
      if (parsed.finalAnswer) {
        if (!toolResult) {
          console.log(`⚠️ 检测到未调用工具直接回答，强制要求调用工具`);
          this.messages.push({ role: "assistant", content });
          this.messages.push({ role: "user", content: '错误：你必须先调用工具获取信息，不能直接回答。请调用合适的工具。' });
          iterations++;
          continue;
        }
        console.log(`✅ 最终答案：${parsed.finalAnswer}`);
        return parsed.finalAnswer;
      }

      // 没有工具调用，也没有最终答案
      console.log(`⚠️ 未检测到有效工具调用`);
      return "无法回答";

      iterations++;
    }

    console.log(`⚠️ 达到最大迭代次数`);
    return "无法获取信息";
  }
}